package com.lch.thread.homework.w0518;

import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: liuchenhui
 * @create: 2019-05-28 23:03
 **/
public class LinkListQueue<T> {

    private LinkedList<T> linkedList = new LinkedList<>();
    private ReentrantLock putLock = new ReentrantLock();
    private ReentrantLock takeLock = new ReentrantLock();
    private Condition noFull = putLock.newCondition();
    private Condition noEm = takeLock.newCondition();

    private volatile int totalNum;

    private volatile AtomicInteger num = new AtomicInteger();// 当前数量

    public LinkListQueue(int totalNum) {
        this.totalNum = totalNum;
    }

    public T take() {
        T t = null;
        try {
            takeLock.lock();
            while (num.get() == 0) {
                noEm.await();
            }
            t = linkedList.poll();
            num.getAndDecrement();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (num.get() == 0) {
                try {
                    putLock.lock();
                    noFull.signalAll();
                    putLock.unlock();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            takeLock.unlock();
        }
        return t;
    }

    public void put(T obj) {
        try {
            putLock.lock();
            while (num.get() == totalNum) {
                noFull.await();
            }
            linkedList.add(obj);
            num.getAndIncrement();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (num.get() > 0) {
                try {
                    takeLock.lock();
                    noEm.signalAll();
                    takeLock.unlock();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            putLock.unlock();
        }
    }
}
